package com.dev.toolkit.config.ds;

import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.*;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

/**
 * @author ZhuYouBin
 * @version 1.0.0
 * @date 2024/5/21 10:21
 * @description 数据源配置
 */
@Configuration
public class DsMyBatisConfig {
    /**
     * 数据源名称
     */
    public static final String DEFAULT_DATA_SOURCE_NAME = "defaultDataSource";
    /**
     * 动态数据源名称
     */
    public static final String DYNAMIC_DATA_SOURCE_NAME = "dynamicDataSource";
    /**
     * 原型数据源名称
     */
    public static final String PROTOTYPE_DATA_SOURCE_NAME = "prototypeDataSource";
    /**
     * SqlSessionFactory 的bean名称
     */
    public static final String SQL_SESSION_FACTORY_NAME = "sqlSessionFactory";
    /**
     * SqlSessionTemplate 的bean名称
     */
    public static final String SQL_SESSION_TEMPLATE_NAME = "sqlSessionTemplate";
    /**
     * 当前这个数据源扫描哪个包路径
     */
    public static final String BASE_PACKAGE = "com.dev.toolkit.repository";
    /**
     * mapper xml 映射文件路径
     */
    public static final String MAPPER_XML_PATH = "classpath*:mappers/*.xml";

    /**
     * 注入动态数据源对象，并且优先使用这个数据源对象
     */
    @Primary
    @Bean(DYNAMIC_DATA_SOURCE_NAME)
    public DataSource dynamicDataSource(@Qualifier(DEFAULT_DATA_SOURCE_NAME) DataSource defaultDataSource) {
        // 设置所有的目标数据源对象
        Map<Object, Object> targetDataSourceMap = new HashMap<>();
        targetDataSourceMap.put(DEFAULT_DATA_SOURCE_NAME, defaultDataSource);
        return new DynamicDataSource(defaultDataSource, targetDataSourceMap);
    }

    /**
     * 注入 defaultDataSource 默认的数据源对象
     */
    @Bean(DEFAULT_DATA_SOURCE_NAME)
    @ConfigurationProperties("spring.datasource")
    public DataSource defaultDataSource() {
        return DruidDataSourceBuilder.create().build();
    }

    /**
     * 创建一个懒加载的原型 Bean 对象，用于动态创建数据源对象，每次获取都会创建一个新的对象
     */
    @Bean(PROTOTYPE_DATA_SOURCE_NAME)
    @Scope("prototype")
    @Lazy
    @ConfigurationProperties("spring.datasource.druid")
    public DataSource prototypeDynamicDataSource() {
        return DruidDataSourceBuilder.create().build();
    }

    /**
     * 配置 SqlSessionFactory 会话工厂，将我们自己写的动态数据源放入到 SqlSessionFactory 会话工厂里面
     * 这样才可以实现动态数据源的切换，因为 MyBatis 是通过 SqlSessionFactory 会话工厂来获取数据源的
     */
    @Bean(SQL_SESSION_FACTORY_NAME)
    public MybatisSqlSessionFactoryBean mybatisSqlSessionFactoryBean(@Qualifier(DYNAMIC_DATA_SOURCE_NAME) DataSource dynamicDataSource) throws Exception {
        MybatisSqlSessionFactoryBean sessionFactory = new MybatisSqlSessionFactoryBean();
        // 配置动态数据源到 SqlSessionFactory 会话工厂里面
        sessionFactory.setDataSource(dynamicDataSource);
        // 指定 mapper.xml 映射文件路径
        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        sessionFactory.setMapperLocations(resolver.getResources(MAPPER_XML_PATH));
        return sessionFactory;
    }

    /**
     * 创建 SqlSessionTemplate 对象，提供了模板化的处理方法
     */
    @Bean(SQL_SESSION_TEMPLATE_NAME)
    public SqlSessionTemplate ds2SqlSessionTemplate(@Qualifier(SQL_SESSION_FACTORY_NAME) SqlSessionFactory sqlSessionFactory) {
        // 创建 SqlSessionTemplate 对象
        return new SqlSessionTemplate(sqlSessionFactory);
    }

    /**
     * Mapper 扫描配置类
     */
    @Bean("mapperScannerConfigurer")
    public MapperScannerConfigurer mapperScannerConfigurer() {
        MapperScannerConfigurer configurer = new MapperScannerConfigurer();
        configurer.setBasePackage(BASE_PACKAGE);
        configurer.setSqlSessionFactoryBeanName(SQL_SESSION_FACTORY_NAME);
        configurer.setSqlSessionTemplateBeanName(SQL_SESSION_TEMPLATE_NAME);
        return configurer;
    }
}